#ifndef ALGORITHM_SEQUENTIAL__
#define ALGORITHM_SEQUENTIAL__

#include <map>
#include <list>
#include <set>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <iomanip>

#include "algorithm.h"
#include "area.h"
#include "options_sequential.h"
#include "call_list.h"

/** Sekvencni algoritmus. Sekvencni algoritmus nejprve setridi vysilace, pote vzdy jeden z nich
 * vybere a priradi mu zvolenou frekvenci. Tyto tri kroky mohou byt parametrizovany, celkem je 
 * mozne dosahnout 56 ruznych kombinaci sekvencnich algoritmu. Dulezitou charakteristikou
 * sekvencnich algoritmu je nenavratnost prirazeni frekvence vysilaci.
 */
class SequentialAlgorithm: public Algorithm {
public:
	/** Defaultni konstruktor sekvencniho algoritmu.
	 * \param o nastaveni sekvencniho algoritmu
	 */
	SequentialAlgorithm(Options * o): Algorithm(o), options((SequentialOptions *)o) {}
	/** Konstruktor rovnou prirazujici i mapu se zadanim.
	 * \param a mapa zadani
	 * \param o nastaveni sekvencniho algoritmu
	 */
	SequentialAlgorithm(Area * a, Options * o): Algorithm(o), options((SequentialOptions *)o) {
		area = a;
	}
	/** Vraci, zda je algoritmus nastaven tak, aby provedl vsechny mozne kombinace nastaveni.
	 * \return true, pokud ano
	 */
	bool AllConfigurations() {
		options->Log() << "Vse: " << options->all;
		return options->all;
	}
	/** Nacte dalsi kombinaci nastaveni algoritmu.
	 * \return true, pokud dalsi kombinace existuje
	 */
	bool Next() {
		/* clear the area and load next configuration */
		area->clear();
		return options->Next();
	}
	bool Solve();
	/** Vypise informace do logu o nastaveni algoritmu.
	 */
	void PrintInfo() {
		options->Log() << "Sekvencni algoritmus fungujici ve dvou krocich: \n - prvotni setrideni posloupnosti prijimacu \n - neustale opakovani: vybrat dalsi prijimac, pridelit mu frekvenci" << std::endl;
		options->Print();
	}
	/** Vrati popisek algoritmu.
	 * \return popis algoritmu
	 */
	std::string GetDetail() {
		return "sequential:" + options->GetTitle();
	}
private:
	SequentialOptions * options; ///< nastaveni sekvencniho algoritmu

	/** Funkce porovnavajici stupne dvou vysilacu.
	 * \param a prvni vysilac
	 * \param b druhy vysilac
	 * \return true, pokud je stupen prvniho vysilace mensi nez stupen druheho
	 */
	bool degreeComparer(int a, int b) {
		return area->cells[area->findCell(a)].info < area->cells[area->findCell(b)].info;
	}
	std::list<int>::iterator mmin_element(std::list<int>::iterator begin, std::list<int>::iterator end);
	std::list<int>::iterator mmax_element(std::list<int>::iterator begin, std::list<int>::iterator end);
	void CountDegrees();
	void GCountDegrees();

	int * orderLargestFirst();
	int * orderLargestFirstExclude();
	int * orderSmallestLast();
	int * orderGLargestFirst();
	int * orderGLargestFirstExclude();
	int * orderGSmallestLast();
	int * orderRandom();
	void frequencySmallest(CallList * call_list);
	void frequencyOccupied(CallList * call_list);
	void frequencySmallestOccupied(CallList * call_list);
	void frequencyHeaviestOccupied(CallList * call_list);
};

#endif
